﻿using Joy.Erp.BasicArchives.Core;
using Joy.Erp.BasicArchives.Core.Filters;
using Joy.Erp.BasicArchives.Permissions;
using Microsoft.AspNetCore.Authorization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Data;
using Volo.Abp.Domain.Repositories;

namespace Joy.Erp.BasicArchives.Sales
{
    [Authorize]
    public class SaleOrderAppService : BasicArchivesAppService, ISaleOrderAppService
    {
        protected ISaleOrderRepository SaleOrderRepository => LazyServiceProvider.LazyGetRequiredService<ISaleOrderRepository>();
        protected SaleOrderManager SaleOrderManager => LazyServiceProvider.LazyGetRequiredService<SaleOrderManager>();

        [Authorize(BasicArchivesPermissions.SaleOrder.Query)]
        public async Task<SaleOrderDto> GetAsync(Guid id)
        {
            var entity = await SaleOrderRepository.GetAsync(id, true);
            return ObjectMapper.Map<SaleOrder, SaleOrderDto>(entity);
        }

        [Authorize(BasicArchivesPermissions.SaleOrder.Query)]
        public async Task<SaleOrderLineDto> GetChildAsync(Guid id)
        {
            var child = await SaleOrderRepository.GetChildAsync(id, true);
            return ObjectMapper.Map<SaleOrderLine, SaleOrderLineDto>(child);
        }

        [Authorize(BasicArchivesPermissions.SaleOrder.Query)]
        public async Task<SaleOrderDto> FindAsync(string filter)
        {
            var entity = await SaleOrderRepository.FindAsync(x => x.Code == filter);
            return ObjectMapper.Map<SaleOrder, SaleOrderDto>(entity);
        }

        [Authorize(BasicArchivesPermissions.SaleOrder.Query)]
        public async Task<SaleOrderDto> FindBySourceKeyAsync(string sourceKey, string sourceType)
        {
            var entity = await SaleOrderRepository.FindAsync(x => x.SourceKey == sourceKey && x.SourceType == sourceType);
            return ObjectMapper.Map<SaleOrder, SaleOrderDto>(entity);
        }

        [Authorize(BasicArchivesPermissions.SaleOrder.Query)]
        public async Task<PagedResultDto<SaleOrderDto>> GetListAsync(FilterRequestDto input)
        {
            var totalCount = await SaleOrderRepository.GetCountAsync(input.Filter, true);
            var list = await SaleOrderRepository.GetPagedListAsync(input.Filter, input.SkipCount, input.MaxResultCount, input.Sorting, true);

            return new PagedResultDto<SaleOrderDto>(
                totalCount,
                ObjectMapper.Map<List<SaleOrder>, List<SaleOrderDto>>(list));
        }

        [Authorize(BasicArchivesPermissions.SaleOrder.Query)]
        public async Task<PagedResultDto<SaleOrderDto>> QueryAsync(FilterGroupRequestDto input)
        {
            Expression<Func<SaleOrder, bool>> expression = null;
            if (input != null && input.FilterGroup != null)
            {
                expression = FilterConverter.Convert<SaleOrder>(input.FilterGroup);
            }

            var totalCount = await SaleOrderRepository.GetCountAsync(expression, true);
            var list = await SaleOrderRepository.GetPagedListAsync(expression, input.SkipCount, input.MaxResultCount, input.Sorting, true);

            return new PagedResultDto<SaleOrderDto>(
                totalCount,
                ObjectMapper.Map<List<SaleOrder>, List<SaleOrderDto>>(list));
        }

        [Authorize(BasicArchivesPermissions.SaleOrder.Query)]
        public async Task<PagedResultDto<SaleOrderLineDto>> QueryChildrenAsync(FilterGroupRequestDto input)
        {
            Expression<Func<SaleOrderLine, bool>> expression = null;
            if (input != null && input.FilterGroup != null)
            {
                expression = FilterConverter.Convert<SaleOrderLine>(input.FilterGroup);
            }

            var totalCount = await SaleOrderRepository.GetChildrenCountAsync(expression, true);
            var list = await SaleOrderRepository.GetChildrenPagedListAsync(expression, input.SkipCount, input.MaxResultCount, input.Sorting, true);

            return new PagedResultDto<SaleOrderLineDto>(
                totalCount,
                ObjectMapper.Map<List<SaleOrderLine>, List<SaleOrderLineDto>>(list));
        }

        [Authorize(BasicArchivesPermissions.SaleOrder.Create)]
        public async Task<SaleOrderDto> CreateAsync(SaleOrderCreateDto input)
        {
            var entity = ObjectMapper.Map<SaleOrderCreateDto, SaleOrder>(input);

            await SaleOrderManager.CreateAsync(entity, true);
            return await GetAsync(entity.Id);
        }

        [Authorize(BasicArchivesPermissions.SaleOrder.Update)]
        public async Task<SaleOrderDto> UpdateAsync(Guid id, SaleOrderUpdateDto input)
        {
            var entity = await SaleOrderRepository.GetAsync(id, true);
            entity.SetConcurrencyStampIfNotNull(input.ConcurrencyStamp);

            await SaleOrderRepository.EnsureCollectionLoadedAsync(entity, x => x.Children);
            ObjectMapper.Map(input, entity);

            await SaleOrderManager.UpdateAsync(entity, true);
            return await GetAsync(entity.Id);
        }

        [Authorize(BasicArchivesPermissions.SaleOrder.Submit)]
        public async Task<SaleOrderDto> SubmitAsync(Guid id)
        {
            await SaleOrderManager.ChangeStatus(id, DocumentStatus.Waiting);
            return await GetAsync(id);
        }

        [Authorize(BasicArchivesPermissions.SaleOrder.Approve)]
        public async Task<SaleOrderDto> ApproveAsync(Guid id)
        {
            await SaleOrderManager.ChangeStatus(id, DocumentStatus.Approved);
            return await GetAsync(id);
        }

        [Authorize(BasicArchivesPermissions.SaleOrder.UnApprove)]
        public async Task<SaleOrderDto> UnApproveAsync(Guid id)
        {
            await SaleOrderManager.ChangeStatus(id, DocumentStatus.Open);
            return await GetAsync(id);
        }

        [Authorize(BasicArchivesPermissions.SaleOrder.Delete)]
        public async Task DeleteAsync(Guid id)
        {
            await SaleOrderManager.DeleteAsync(id);
        }

        [Authorize(BasicArchivesPermissions.SaleOrder.Create)]
        public async Task<List<SaleOrderDto>> BatchCreateAsync(List<SaleOrderCreateDto> inputs, bool useBulk = false)
        {
            var entities = inputs
                .Select(ObjectMapper.Map<SaleOrderCreateDto, SaleOrder>)
                .ToList();

            await SaleOrderManager.CreateManyAsync(entities, useBulk, autoSave: true);
            return ObjectMapper.Map<List<SaleOrder>, List<SaleOrderDto>>(entities);
        }

        [Authorize(BasicArchivesPermissions.SaleOrder.Update)]
        public async Task<List<SaleOrderDto>> BatchUpdateAsync(List<SaleOrderBatchUpdateDto> inputs, bool useBulk = false)
        {
            if (inputs.Count == 0) return new List<SaleOrderDto>();

            var ids = inputs.Select(x => x.Id).ToList();
            var query = await SaleOrderRepository.WithDetailsAsync(x => x.Children);
            var entities = await AsyncExecuter.ToListAsync(query.Where(x => ids.Contains(x.Id)));

            if (useBulk)
            {
                foreach (var entity in entities)
                {
                    var input = inputs.FirstOrDefault(x => entity.Id == x.Id);
                    if (input == null) continue;

                    entity.Code = input.Data.Code;
                    entity.BusinessDate = input.Data.BusinessDate;
                    entity.DocumentStatus = input.Data.DocumentStatus;
                    entity.SourceCreatedOn = input.Data.SourceCreatedOn;

                    entity.OrderTypeId = input.Data.OrderTypeId;
                    entity.CustomerId = input.Data.CustomerId;
                    entity.OperatorId = input.Data.OperatorId;
                    entity.Mark = input.Data.Mark;

                    // 删除逻辑：删除源数据中不存在的实体
                    var childrenToRemove = entity.Children.Where(destChild =>
                    !input.Data.Children.Any(srcChild => srcChild.Id.HasValue && srcChild.Id.Value == destChild.Id)).ToList();
                    foreach (var child in childrenToRemove)
                    {
                        entity.Children.Remove(child);
                    }

                    // 更新和新增子项逻辑
                    foreach (var srcChild in input.Data.Children)
                    {
                        var existingChild = srcChild.Id.HasValue && srcChild.Id.Value != Guid.Empty
                            ? entity.Children.FirstOrDefault(destItem => destItem.Id == srcChild.Id.Value)
                            : null;

                        if (existingChild != null)
                        {
                            existingChild.LineNo = srcChild.LineNo;
                            existingChild.ItemMasterId = srcChild.ItemMasterId;
                            existingChild.ItemCode = srcChild.ItemCode;
                            existingChild.ItemName = srcChild.ItemName;
                            existingChild.ItemSpec = srcChild.ItemSpec;
                            existingChild.Quantity = srcChild.Quantity;
                            existingChild.UnitId = srcChild.UnitId;
                            existingChild.UnitPrice = srcChild.UnitPrice;
                            existingChild.DeliveryDate = srcChild.DeliveryDate;
                            existingChild.SourceCreatedOn = srcChild.SourceCreatedOn;
                        }
                        else
                        {
                            var newLine = new SaleOrderLine
                            {
                                SourceType = srcChild.SourceType,
                                SourceKey = srcChild.SourceKey,
                                SourceCreatedOn = srcChild.SourceCreatedOn,
                                LineNo = srcChild.LineNo,
                                ItemMasterId = srcChild.ItemMasterId,
                                ItemCode = srcChild.ItemCode,
                                ItemName = srcChild.ItemName,
                                ItemSpec = srcChild.ItemSpec,
                                Quantity = srcChild.Quantity,
                                UnitId = srcChild.UnitId,
                                UnitPrice = srcChild.UnitPrice,
                                DeliveryDate = srcChild.DeliveryDate,
                                DocumentId = entity.Id,
                                CreationTime = Clock.Now,
                                CreatorId = CurrentUser.Id,
                            };

                            newLine.SetId(GuidGenerator.Create());
                            entity.Children.Add(newLine);
                        }
                    }
                }

                using (var uow = UnitOfWorkManager.Begin(new Volo.Abp.Uow.AbpUnitOfWorkOptions(), requiresNew: true))
                {
                    await SaleOrderRepository.BulkUpdateAsync(entities);
                    await uow.CompleteAsync();
                }
            }
            else
            {
                foreach (var entity in entities)
                {
                    var input = inputs.FirstOrDefault(x => entity.Id == x.Id);
                    if (input == null) continue;

                    entity.SetConcurrencyStampIfNotNull(input.Data.ConcurrencyStamp);
                    ObjectMapper.Map(input.Data, entity);
                }
                await SaleOrderRepository.UpdateManyAsync(entities);
            }

            return ObjectMapper.Map<List<SaleOrder>, List<SaleOrderDto>>(entities);
        }

        [Authorize(BasicArchivesPermissions.SaleOrder.Delete)]
        public async Task BatchDeleteAsync(List<Guid> ids, bool useBulk = false)
        {
            if (ids.Count == 0) return;

            var entities = new List<SaleOrder>();
            if (useBulk)
            {
                var query = await SaleOrderRepository.WithDetailsAsync(x => x.Children);
                entities = await AsyncExecuter.ToListAsync(query.Where(x => ids.Contains(x.Id)));
            }
            else
            {
                var allEntities = new List<SaleOrder>();
                foreach (var id in ids)
                {
                    var entity = await SaleOrderRepository.GetAsync(id, true);
                    await SaleOrderRepository.EnsureCollectionLoadedAsync(entity, x => x.Children);
                    entities.Add(entity);
                }
            }

            await SaleOrderManager.DeleteManyAsync(entities, useBulk, autoSave: true);
        }
    }
}